﻿Namespace CryptoProviders
    Friend Class B2B1
        Private mKeySize As Integer '= 1024 '8192 bit key
        Private mSeedBlockSize As Integer = 64
        Private mSeedBlockCount As Integer = 5

        Private mKey() As Byte
        Private mIV As Integer
        Private mCurrentPos As Integer

        Public ReadOnly Code As String = "B2B1"

        Public ReadOnly Property Key() As Byte()
            Get
                Return mKey
            End Get
        End Property
        Public ReadOnly Property IV() As Integer
            Get
                Return mIV
            End Get
        End Property

        Public Sub New()
            mKeySize = mSeedBlockSize * mSeedBlockCount  ' * (10 + CInt(5 * Rnd())) ' ' 1024 + (CInt(64 * Rnd()) - 32) 'default 8192 bit key +/- random 256 bit
            mIV = CInt((mKeySize - 1) * Rnd())
            mCurrentPos = mIV
            GenerateKey()
        End Sub
        Public Sub New(ByVal seed As Generic.List(Of Byte()))
            SetSeed(seed)
        End Sub
        Public Sub SetSeed(ByVal seed As Generic.List(Of Byte()))
            Dim aKey As New Generic.List(Of Byte)
            For i As Integer = 0 To seed.Count - 2
                aKey.AddRange(seed(i))
            Next
            mSeedBlockSize = seed(0).Length
            mSeedBlockCount = seed.Count - 1
            mKey = aKey.ToArray
            mKeySize = mKey.Length
            Using clsIO As New System.IO.MemoryStream(seed(seed.Count - 1))
                mIV = Framework.IO.BlockStreamReader.ReadInteger(clsIO)
                mCurrentPos = mIV
            End Using
        End Sub
        Public Function GetSeed() As Generic.List(Of Byte())
            Dim aRet As New Generic.List(Of Byte())
            For i As Integer = 0 To mKey.Length - 1 Step mSeedBlockSize
                Dim aTmp As New Generic.List(Of Byte)
                For j As Integer = i To i + mSeedBlockSize - 1
                    aTmp.Add(mKey(j))
                Next
                aRet.Add(aTmp.ToArray)
            Next

            Using clsIO As New System.IO.MemoryStream()
                Framework.IO.BlockStreamWriter.Write(clsIO, mIV)
                aRet.Add(clsIO.ToArray)
            End Using
            Return aRet
        End Function

        Private Sub GenerateKey()
            ReDim mKey(mKeySize - 1)
            For i As Integer = 0 To mKey.Length - 1
                mKey(i) = CByte(Rnd() * 255)
            Next
        End Sub

        Public Sub Encrypt(ByVal data() As Byte)
            If data Is Nothing Then Return
            EncryptWorker(data, 0, data.Length)
        End Sub
        Public Sub Encrypt(ByVal data() As Byte, ByVal offset As Integer)
            If data Is Nothing Then Return
            EncryptWorker(data, offset, data.Length - offset)
        End Sub
        Public Sub Encrypt(ByVal data() As Byte, ByVal offset As Integer, ByVal count As Integer)
            If data Is Nothing Then Return
            EncryptWorker(data, offset, count)
        End Sub
        Private Sub EncryptWorker(ByVal data() As Byte, ByVal offset As Integer, ByVal count As Integer)
            Dim aKey() As Byte = mKey
            Dim iPos As Integer = mCurrentPos
            Dim iKeyLen As Integer = aKey.Length
            For i As Integer = offset To offset + count - 1
                data(i) = data(i) Xor aKey(iPos)
                iPos += 1
                If iPos = iKeyLen Then iPos = 0
            Next
            mCurrentPos = iPos
        End Sub
        Public Sub Decrypt(ByVal data() As Byte)
            If data Is Nothing Then Return
            Decrypt(data, 0, data.Length)
        End Sub
        Public Sub Decrypt(ByVal data() As Byte, ByVal offset As Integer, ByVal count As Integer)
            If data Is Nothing Then Return
            Dim aKey() As Byte = mKey
            Dim iPos As Integer = mCurrentPos
            Dim iKeyLen As Integer = aKey.Length
            For i As Integer = offset To offset + count - 1
                data(i) = data(i) Xor aKey(iPos)
                iPos += 1
                If iPos = iKeyLen Then iPos = 0
            Next
            mCurrentPos = iPos
        End Sub
    End Class
End Namespace